#include <seminix/vmacache.h>
#include <seminix/pgtable.h>

#define VMACACHE_SHIFT	PMD_SHIFT
#define VMACACHE_HASH(addr) ((addr >> VMACACHE_SHIFT) & VMACACHE_MASK)

static inline bool vmacache_valid_mm(struct tcb *tsk, struct mm_struct *mm)
{
    if (tsk)
        return tsk->mm == mm;
    return false;
}

void __vmacache_update(struct tcb *tsk, unsigned long addr, struct vm_area_struct *newvma)
{
    if (vmacache_valid_mm(tsk, newvma->vm_mm))
        tsk->vmacache.vmas[VMACACHE_HASH(addr)] = newvma;
}

static bool vmacache_valid(struct tcb *tsk, struct mm_struct *mm)
{
    if (!vmacache_valid_mm(tsk, mm))
        return false;

    if (mm->vmacache_seqnum != tsk->vmacache.seqnum) {
        tsk->vmacache.seqnum = mm->vmacache_seqnum;
        vmacache_flush(tsk);
        return false;
    }
    return true;
}

struct vm_area_struct *__vmacache_find(struct tcb *tsk, struct mm_struct *mm, unsigned long addr)
{
    int idx = VMACACHE_HASH(addr);
    int i;

    if (!vmacache_valid(tsk, mm))
        return NULL;

    for (i = 0; i < (int)VMACACHE_SIZE; i++) {
        struct vm_area_struct *vma = tsk->vmacache.vmas[idx];

        if (vma) {
            if (vma->vm_start <= addr && vma->vm_end > addr)
                return vma;
        }
        if (++idx == VMACACHE_SIZE)
            idx = 0;
    }
    return NULL;
}
